/**
 * 
 */
package org.dragonfire.parser.functions;

import java.util.HashMap;

import org.dragonfire.parser.Equation;
import org.dragonfire.parser.exceptions.ParseException;
import org.dragonfire.util.DMath;

/**
 * @author Darth Android
 * @date Jul 28, 2006
 */
public abstract class Function {

	private static HashMap<String, Function> funcs = new HashMap<String, Function>();

	public static Function getInstance(String name) {
		if (!funcs.containsKey(name.toUpperCase()))
			loadInternalFunction(name.toUpperCase().charAt(0) + name.toLowerCase().substring(1));
		return funcs.get(name.toUpperCase());
	}

	public static void putFunction(String name, Function function) {
		name = name.toUpperCase();
		if (!funcs.containsKey(name))
			funcs.put(name, function);
		else
			throw new ParseException("FUNCTION_EXISTS", name);
	}

	public static void loadFunction(String name) {
		try {
			Class function = ClassLoader.getSystemClassLoader().loadClass(name);
			Function f = (Function) function.newInstance();
			name = f.getClass().getSimpleName().toUpperCase();
			putFunction(name, f);
		} catch (ClassNotFoundException cnfe) {
			throw new ParseException("UNKNOWN_FUNCTION", name);
		} catch (Exception e) {
			throw new ParseException("BAD_FUNCTION", name);
		}
	}

	public static void loadInternalFunction(String name) {
		loadFunction("org.dragonfire.parser.functions." + name);
	}

	public static String parseFunction(Equation equ, String name, String[] params) {
		name = name.toUpperCase();
		if (!funcs.containsKey(name))
			try {
				loadInternalFunction(name.charAt(0) + name.toLowerCase().substring(1));
			} catch (Exception e) {
				throw new ParseException("UNKNOWN_FUNCTION", name);
			}
		Function func = funcs.get(name);
		if (params.length < func.minParams())
			throw new ParseException("INCORRECT_PARAMS", name, func.minParams(), params.length);
		try {
			return func.eval(equ, params);
		} catch (Exception e) {
			if (e instanceof ParseException)
				throw (ParseException) e;
			throw new ParseException("INTERNAL_ERROR", name);
		}
	}

	public static void createGenericFunction(String name, String equation, int minParams) {
		name = name.toUpperCase();
		Generic func = new Generic(equation, minParams);
		funcs.put(name, func);
	}

	public static double toDouble(String value) {
		return DMath.parseDouble(value);
	}

	public abstract int minParams();

	public abstract String eval(Equation equ, String[] params);
}
